/**
@author		Jong Min, Lee
@date		  02/09/2014
@brief		study tab control

copyright(c) Tae Young Soft. All right reserved. 2014
*/

#include "stdafx.h"
#include "Taurus_Viewer/StudyTab.h"

#include <algorithm>

#include "Taurus_Viewer/StudyTabProperty.h"

#define TIMER_ID_LEFT_SCROLL_CLICK    1
#define TIMER_ID_LEFT_SCLLOING        2
#define TIMER_ID_RIGHT_SCROLL_CLICK   3
#define TIMER_ID_RIGHT_SCROLLING      4

IMPLEMENT_DYNAMIC(CStudyTab, CWnd)

CStudyTab::CStudyTab()
: m_theCloseBtnItem(reinterpret_cast<const CStudyTabItem*>(1))
, m_theMenuBtnItem(reinterpret_cast<const CStudyTabItem*>(2))
, m_theLeftScrollBtnItem(reinterpret_cast<const CStudyTabItem*>(3))
, m_theRightScrollBtnItem(reinterpret_cast<const CStudyTabItem*>(4))
, m_theCurSelItem(nullptr)
, m_theHoverdItem(nullptr)
, m_thePushedItem(nullptr)
, m_theItemsOffset(0)
, m_isPartialView(FALSE)
, m_isScrollLeftAllowd(FALSE)
, m_isScrollRightAllowd(FALSE)
, m_theToolTipCtrl(nullptr)
, m_theDrawManger(nullptr)
, m_theVisibleCloseBtn(FALSE)
, m_theVisibleMenuBtn(FALSE)
, m_theVisibleScrollBtns(TRUE)
, m_theToolTipEnable(TRUE)
, m_theScrollingStep(15)
, m_theSysImageSize(0, 0)
, m_theMaxNumOfTabs(5)
{
  m_theRearrangeManager = &m_theSerivce;
  m_theBehaviorManager = &m_theSerivce;
  m_theToolTipManager = &m_theSerivce;
}

CStudyTab::~CStudyTab()
{
  if(m_theFont.m_hObject) m_theFont.DeleteObject();

  removeAllItems();
}

void CStudyTab::removeAllItems()
{
  for(auto pos = m_theItemList.begin(), end = m_theItemList.end();
      pos != end;
      ++pos)
  {
    _DELETE(*pos);
  }
  m_theItemList.clear();
  StopScrolling();
  m_theCurSelItem = m_theHoverdItem = m_thePushedItem = nullptr;
  m_theItemsOffset = 0;
  m_isPartialView = m_isScrollLeftAllowd = m_isScrollRightAllowd = FALSE;
}

void CStudyTab::update(BOOL bRedraw)
{
  if(::IsWindow(GetSafeHwnd()))
  {
    rearrange();
    if(bRedraw)
      Invalidate(FALSE);
  }
}

void CStudyTab::updateTabProperty(BOOL redraw)
{
  if(!::IsWindow(GetSafeHwnd())) return;

  CStudyTabProperty* TP = CStudyTabProperty::getInstance();
  CStudyTabProperty::CFontProperty fontProp = TP->getFontProperty(m_theResType);
  if(m_theFont.m_hObject) m_theFont.DeleteObject();
  m_theFont.CreateFont(fontProp.thePointSize, 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS,
    CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontProp.theFaceName);

  m_theMaxNumOfTabs = TP->getMaxNumTabs();

  rearrange();
  if(redraw)
    Invalidate(FALSE);
}

void CStudyTab::installStyle(IStudyTabStyle* style)
{
  if(style != nullptr)
  {
    setDrawManager(style->getDrawManager());
    setRearrangeManager(style->getRearrangeManager());
    setBehaviorManager(style->getBehaviorManager());
    setToolTipManager(style->getToolTipManager());
  }
  else
  {
    setDrawManager(nullptr);
    setRearrangeManager(nullptr);
    setBehaviorManager(nullptr);
    setToolTipManager(nullptr);
  }
}

void CStudyTab::setDrawManager(CStudyTabDraw* manager)
{
  m_theDrawManger = manager;
}

CStudyTabDraw* CStudyTab::getDrawManager() const
{
  return m_theDrawManger;
}

void CStudyTab::setRearrangeManager(IStudyTabRearrange* p)
{
  m_theRearrangeManager = (p != nullptr ? p : &m_theSerivce);
}

IStudyTabRearrange* CStudyTab::getRearrangleManager() const
{
  return m_theRearrangeManager;
}

void CStudyTab::setBehaviorManager(IStudyTabBehavior* p)
{
  m_theBehaviorManager = (p != nullptr ? p : &m_theSerivce);
}

IStudyTabBehavior* CStudyTab::getBehaviorManager() const
{
  return m_theBehaviorManager;
}

void CStudyTab::setToolTipManager(IStudyTabToolTip* p)
{
  m_theToolTipManager = (p != nullptr ? p : &m_theSerivce);
}

IStudyTabToolTip* CStudyTab::getToolTipManager() const
{
  return m_theToolTipManager;
}

BOOL CStudyTab::setSystemImageList(CImageList* imageList)
{

  if(m_theSysImageList.m_hImageList != nullptr)
    m_theSysImageList.DeleteImageList();

  if(imageList != nullptr && imageList->m_hImageList != nullptr)
  {
    if(!m_theSysImageList.Create(imageList) ||
      !::ImageList_GetIconSize(m_theSysImageList.m_hImageList, (int*)&m_theSysImageSize.cx, (int*)&m_theSysImageSize.cy))
    {
      if(m_theSysImageList.m_hImageList != nullptr)
        m_theSysImageList.DeleteImageList();

      m_theSysImageSize.cx = m_theSysImageSize.cy = 0;
      return FALSE;
    }
  }
  else
  {
    m_theSysImageSize.cx = m_theSysImageSize.cy = 0;
  }

  return TRUE;
}

const CImageList* CStudyTab::getSystemImageList() const
{
  if(m_theSysImageList.m_hImageList != nullptr)
    return &m_theSysImageList;

  return nullptr;
}

CSize CStudyTab::getSystemImageSize() const
{
  return m_theSysImageSize;
}

void CStudyTab::enableToolTip(BOOL enable)
{
  m_theToolTipEnable = enable;
}

BOOL CStudyTab::isToolTipEnalbe() const
{
  return m_theToolTipEnable;
}

unsigned CStudyTab::getNumOfItems() const
{
  return m_theItemList.size();
}

const CStudyTabItem* CStudyTab::getItem(unsigned index) const
{
  if(index >= getNumOfItems()) return nullptr;

  auto itemPos = m_theItemList.begin();
  std::advance(itemPos, index);
  return *itemPos;
}

DWORD CStudyTab::setSel(unsigned index)
{
  if(index >= getNumOfItems()) return ERROR_INVALID_PARAMETER;

  //get the position of item
  auto itemPos = m_theItemList.begin();
  std::advance(itemPos, index);

  m_theCurSelItem = *itemPos;
  return ERROR_SUCCESS;
}

const CStudyTabItem* CStudyTab::getSel() const
{
  return m_theCurSelItem;
}

const CStudyTabItem* CStudyTab::getHover() const
{
  return m_theHoverdItem;
}

const CStudyTabItem* CStudyTab::getPushed() const
{
  return m_thePushedItem;
}

const CStudyTabItem* CStudyTab::hitTest(const CPoint& point)
{
  return m_theBehaviorManager->hitTest(*this, m_theSerivce, point);
}

int CStudyTab::getIndex(const CStudyTabItem* item) const
{
  unsigned index = 0;
  for(auto pos = m_theItemList.cbegin(), end = m_theItemList.cend();
      pos != end;
      ++pos, ++index)
  {
    if(*pos == item) break;
  }

  if(index < getNumOfItems())
    return static_cast<int>(index);

  return -1;
}

BOOL CStudyTab::isExist(const CStudyTabItem* item) const
{
  auto pos = find(m_theItemList.begin(), m_theItemList.end(), item);

  return pos != m_theItemList.end();
}

BOOL CStudyTab::isItemVisible(const CStudyTabItem* item, BOOL* isPartially) const
{
  if(!isExist(item)) return FALSE;

  const CRect& rc = item->rect;

  if(rc.right < m_theItemsRect.left || rc.left >= m_theItemsRect.right)
  {
    if(isPartially != nullptr) *isPartially = FALSE;
    return FALSE;
  }
  else
  {
    if(isPartially != nullptr)
      *isPartially = (rc.left < m_theItemsRect.left || rc.right > m_theItemsRect.right);
    return TRUE;
  }
}

void CStudyTab::ensureVisible(const CStudyTabItem* item)
{
  if(item->rect.left < m_theItemsRect.left)
    m_theItemsOffset -= m_theItemsRect.left - item->rect.left;
  else
    m_theItemsOffset += item->rect.right - m_theItemsRect.right;
}

void CStudyTab::scrollToBegin()
{
  m_theItemsOffset = 0;
}

void CStudyTab::scrollToEnd()
{
  m_theItemsOffset = m_theMaxItemsOffset;
}

void CStudyTab::showCloseButton(BOOL show)
{
  m_theVisibleCloseBtn = show;
}

BOOL CStudyTab::isCloseButtonVisible() const
{
  return m_theVisibleCloseBtn;
}

void CStudyTab::showMenuButton(BOOL show)
{
  m_theVisibleMenuBtn = show;
}

BOOL CStudyTab::isMenuButtonVisible() const
{
  return m_theVisibleMenuBtn;
}

void CStudyTab::showScrollButtons(BOOL show)
{
  m_theVisibleScrollBtns = show;
}

BOOL CStudyTab::areScrollButtonsVisible() const
{
  return m_theVisibleScrollBtns;
}

CRect CStudyTab::getItemsRect() const
{
  return m_theItemsRect;
}

CRect CStudyTab::getCloseButtonRect() const
{
  return m_theCloseBtnRect;
}

CRect CStudyTab::getMenuButtonRect() const
{
  return m_theMenuBtnRect;
}

CRect CStudyTab::getScrollLeftRect() const
{
  return m_theScrollLeftBtnRect;
}

CRect CStudyTab::getScrollRightRect() const
{
  return m_theScrollRightBtnRect;
}

CRect CStudyTab::getItemHorzMargin()
{
  return m_theRearrangeManager->getItemHorzMargin(*this, m_theSerivce);
}

CRect CStudyTab::getCloseBtnMargin()
{
  return m_theRearrangeManager->getCloseBtnHorzMargin(*this, m_theSerivce);
}

CRect CStudyTab::getMenuBtnMargin()
{
  return m_theRearrangeManager->getMenuBtnHorzMargin(*this, m_theSerivce);
}

CRect CStudyTab::getLeftScrollBtnHorzMargin()
{
  return m_theRearrangeManager->getLeftScrollBtnHorzMargin(*this, m_theSerivce);
}

CRect CStudyTab::getRightScrollBtnHorzMargin()
{
  return m_theRearrangeManager->getRightScrollBtnHorzMargin(*this, m_theSerivce);
}

CToolTipCtrl* CStudyTab::getToolTip() const
{
  return m_theToolTipCtrl;
}

void CStudyTab::setSel(const CStudyTabItem* item)
{
  ASSERT(isExist(item));

  if(m_theCurSelItem == item) return;

  m_theCurSelItem = item;
}

void CStudyTab::rearrange()
{
  CRect rcClent;
  GetClientRect(&rcClent);

  unsigned numOfItems = getNumOfItems();
  if(numOfItems > 0)
  {
    if(m_theCurSelItem != nullptr && !isExist(m_theCurSelItem))
      m_theCurSelItem = nullptr;

    m_theItemsRect = rcClent;

    BOOL visibleCloseBtn = (m_theVisibleScrollBtns &&
                            m_theSysImageList.m_hImageList != nullptr);
    BOOL visibleMenuBtn = (m_theVisibleMenuBtn &&
                           m_theSysImageList.m_hImageList != nullptr);
    BOOL visibleScrollBtns = (m_theVisibleScrollBtns &&
                              m_theSysImageList.m_hImageList != nullptr);

    int sysImagePosY = (m_theItemsRect.top + m_theItemsRect.bottom - m_theSysImageSize.cy) / 2;
    
    if(visibleCloseBtn)
    {
      CRect margin = getCloseBtnMargin();

      m_theCloseBtnRect =  m_theItemsRect;
      m_theCloseBtnRect.right -= margin.right;
      m_theCloseBtnRect.left = m_theCloseBtnRect.right - m_theSysImageSize.cx;
      m_theItemsRect.right = m_theCloseBtnRect.left - margin.left;

      m_theCloseBtnRect.top = sysImagePosY;
      m_theCloseBtnRect.bottom = m_theCloseBtnRect.top + m_theSysImageSize.cy;
    }
    else
    {
      m_theCloseBtnRect.SetRectEmpty();
    }

    if(visibleMenuBtn)
    {
      CRect margin = getMenuBtnMargin();

      m_theMenuBtnRect = m_theItemsRect;
      m_theMenuBtnRect.right -= margin.right;
      m_theMenuBtnRect.left = m_theMenuBtnRect.right - m_theSysImageSize.cx;
      m_theItemsRect.right = m_theMenuBtnRect.left - margin.left;

      m_theMenuBtnRect.top = sysImagePosY;
      m_theMenuBtnRect.bottom = m_theMenuBtnRect.top + m_theSysImageSize.cy;
    }
    else
    {
      m_theMenuBtnRect.SetRectEmpty();
    }

    if(visibleScrollBtns)
    {
      CRect leftScrollMargin = getLeftScrollBtnHorzMargin();
      CRect rightScrollMargin = getRightScrollBtnHorzMargin();

      m_theScrollRightBtnRect = m_theItemsRect;
      m_theScrollRightBtnRect.right -= rightScrollMargin.right;
      m_theScrollRightBtnRect.left = m_theScrollRightBtnRect.right - m_theSysImageSize.cx;
      
      m_theScrollLeftBtnRect.right = m_theScrollRightBtnRect.left - rightScrollMargin.left - leftScrollMargin.right;
      m_theScrollLeftBtnRect.left = m_theScrollLeftBtnRect.right - m_theSysImageSize.cx;

      m_theItemsRect.right = m_theScrollLeftBtnRect.left - leftScrollMargin.left;

      m_theScrollLeftBtnRect.top = m_theScrollRightBtnRect.top = sysImagePosY;
      m_theScrollLeftBtnRect.bottom = m_theScrollRightBtnRect.bottom = m_theScrollLeftBtnRect.top + m_theSysImageSize.cy;
    }
    else
    {
      m_theScrollLeftBtnRect.SetRectEmpty();
      m_theScrollRightBtnRect.SetRectEmpty();
    }

    calcItemsWidth();

    int visibleTabsWidth = max(0, m_theItemsRect.Width() - 1);
    int fullItemsWidth = getFullItemsWidth();

    m_theMaxItemsOffset = max(0, fullItemsWidth - visibleTabsWidth);

    if(m_theItemsOffset < 0) m_theItemsOffset = 0;
    if(m_theItemsOffset > m_theMaxItemsOffset) m_theItemsOffset = m_theMaxItemsOffset;

    m_isPartialView = (m_theMaxItemsOffset > 0);
    m_isScrollLeftAllowd = (m_isPartialView && m_theItemsOffset > 0);
    m_isScrollRightAllowd = (m_isPartialView && m_theItemsOffset < m_theMaxItemsOffset);

    calcItemsRect();
  }
  else
  {
    m_theItemsRect.SetRectEmpty();
    m_theCloseBtnRect.SetRectEmpty();
    m_theMenuBtnRect.SetRectEmpty();
    m_theScrollLeftBtnRect.SetRectEmpty();
    m_theScrollRightBtnRect.SetRectEmpty();
  }

  if(m_theToolTipCtrl != nullptr && m_theToolTipCtrl->m_hWnd != nullptr)
  {
    if(numOfItems > 0)
      m_theToolTipCtrl->SetToolRect(this, 1, &rcClent);
    else
      m_theToolTipCtrl->SetToolRect(this, 1, &m_theItemsRect);
  }
}

int CStudyTab::getFullItemsWidth()
{
  int width = 0;
  for_each(m_theItemList.begin(), m_theItemList.end(),
    [&width](CStudyTabItem* item)
  {
    width += item->width;
  });

  CRect itemHorzMargin = getItemHorzMargin();
  return width + m_theItemList.size() * (itemHorzMargin.left + itemHorzMargin.right);
}

void CStudyTab::calcItemsWidth()
{
  CRect rcItems;

  int itemsWidth = m_theItemsRect.Width();

  CRect itemHorzMargin = getItemHorzMargin();

  int itemWidth = (itemsWidth / m_theMaxNumOfTabs) -
                  ((itemHorzMargin.left + itemHorzMargin.right) * m_theMaxNumOfTabs);

  for_each(m_theItemList.begin(), m_theItemList.end(),
    [&itemWidth](CStudyTabItem* item)
  {
    item->width = itemWidth;
  });
}

void CStudyTab::calcItemsRect()
{
  CRect itemHorzMargin = getItemHorzMargin();

  int pos = m_theItemsRect.left - m_theItemsOffset;

  for(auto i = m_theItemList.begin(), end = m_theItemList.end();
      i != end;
      ++i)
  {
    (*i)->rect = m_theItemsRect;
    (*i)->rect.left = pos;
    (*i)->rect.right = pos += (itemHorzMargin.left + (*i)->width + itemHorzMargin.right);
    (*i)->rect.DeflateRect(itemHorzMargin.left, 0, itemHorzMargin.right, 0);
  }
}

BOOL CStudyTab::isSystemButton(const CStudyTabItem* item) const
{
  return item == m_theCloseBtnItem || item == m_theMenuBtnItem ||
         item == m_theLeftScrollBtnItem || item == m_theRightScrollBtnItem;
}

void CStudyTab::StepLeft()
{
  m_theItemsOffset -= m_theScrollingStep;
}

void CStudyTab::StepRight()
{
  m_theItemsOffset += m_theScrollingStep;
}

void CStudyTab::StopScrolling()
{
  if(m_thePushedItem != nullptr && isSystemButton(m_thePushedItem))
  {
    if(m_thePushedItem == m_theLeftScrollBtnItem)
    {
      KillTimer(TIMER_ID_LEFT_SCROLL_CLICK);
      KillTimer(TIMER_ID_LEFT_SCLLOING);
    }
    else if(m_thePushedItem == m_theRightScrollBtnItem)
    {
      KillTimer(TIMER_ID_RIGHT_SCROLL_CLICK);
      KillTimer(TIMER_ID_RIGHT_SCROLLING);
    }

    m_thePushedItem = nullptr;
    if(::GetCapture() == m_hWnd) ::ReleaseCapture();
  }
}

void CStudyTab::assignHoverItem(const CPoint& point)
{
  const CStudyTabItem* oldHoverItem = m_theHoverdItem;
  m_theHoverdItem = nullptr;

  if(m_theCloseBtnRect.PtInRect(point))
    m_theHoverdItem = m_theCloseBtnItem;
  else if( m_theMenuBtnRect.PtInRect(point))
    m_theHoverdItem = m_theMenuBtnItem;
  else if(m_theScrollLeftBtnRect.PtInRect(point))
    m_theHoverdItem = m_theLeftScrollBtnItem;
  else if(m_theScrollRightBtnRect.PtInRect(point))
    m_theHoverdItem = m_theRightScrollBtnItem;

  if(m_theHoverdItem = nullptr)
    m_theHoverdItem = hitTest(point);

  if(oldHoverItem != m_theHoverdItem)
  {
    CRect oldHoverRect = getItemRect(oldHoverItem);
    CRect curHoverRect = getItemRect(m_theHoverdItem);
    CRect invalidRect;
    invalidRect.IntersectRect(oldHoverRect, curHoverRect);
    RedrawWindow(&invalidRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN);

    if(m_theToolTipCtrl != nullptr && m_theToolTipCtrl->m_hWnd != nullptr)
      m_theToolTipCtrl->Pop();
  }

  TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd, 0};
  ::TrackMouseEvent(&tme);
}

void CStudyTab::ttnNeedText(NMTTDISPINFO* TT)
{
  m_theToolTip = _T("");;
  if(m_theHoverdItem)
  {
    if(m_theHoverdItem == m_theCloseBtnItem) m_theToolTip = m_theToolTipBtnClose;
    else if(m_theHoverdItem == m_theMenuBtnItem) m_theToolTip = m_theToolTipBtnMenu;
    else if(m_theHoverdItem == m_theLeftScrollBtnItem) m_theToolTip = m_theToolTipBtnLeftScroll;
    else if(m_theHoverdItem == m_theRightScrollBtnItem) m_theToolTip = m_theToolTipBtnRightScroll;
    else
    {
      CRect rc(m_theHoverdItem->rect);
      rc.left = max(rc.left, m_theItemsRect.left);
      rc.right = max(rc.right, m_theItemsRect.right);

      if(!rc.IsRectEmpty())
      {
        if(!m_theHoverdItem->theToolTip.IsEmpty())
          m_theToolTip = m_theHoverdItem->theToolTip;
        else
          m_theToolTip = m_theHoverdItem->theTitle;
      }
    }
  }
  TT->lpszText = (TCHAR*)(const TCHAR*)m_theToolTip;
}

CRect CStudyTab::getItemRect(const CStudyTabItem* item) const
{
  CRect rect;
  if(item == nullptr)
    rect = CRect(0,0,0,0);
  else if(item == m_theCloseBtnItem)
    rect = m_theCloseBtnRect;
  else if(item == m_theMenuBtnItem)
    rect = m_theMenuBtnRect;
  else if(item == m_theLeftScrollBtnItem)
    rect = m_theScrollLeftBtnRect;
  else if(item == m_theRightScrollBtnItem)
    rect = m_theScrollRightBtnRect;
  else
    rect  = item->rect;

  return rect;
}

BOOL CStudyTab::validateCreateParams(const CreateParams& params) const
{
  if(params.theResType != MonitorResolution_Normal &&
     params.theResType != MonitorResolution_3M &&
     params.theResType != MonitorResolution_5M)
     return FALSE;

  return TRUE;
}
  
BEGIN_MESSAGE_MAP(CStudyTab, CWnd)
  ON_WM_CREATE()
  ON_WM_DESTROY()
  ON_WM_SIZE()
  ON_WM_PAINT()
  ON_WM_ERASEBKGND()
  ON_WM_LBUTTONDOWN()
  ON_WM_MOUSEMOVE()
  ON_WM_LBUTTONUP()
  ON_WM_MOUSELEAVE()
  ON_WM_TIMER()
  ON_WM_CAPTURECHANGED()
END_MESSAGE_MAP()

// CStudyTab message handlers

int CStudyTab::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

  CreateParams* params = reinterpret_cast<CreateParams*>(lpCreateStruct->lpCreateParams);
  if(params == NULL || !validateCreateParams(*params))
    return -1;

  m_theToolTipCtrl = m_theToolTipManager->createToolTip(*this, m_theSerivce);

  if(m_theToolTipCtrl != nullptr && m_theToolTipCtrl->m_hWnd != nullptr)
  {
    CRect rc(0, 0, 0, 0);
    if(!m_theToolTipCtrl->AddTool(this, LPSTR_TEXTCALLBACK, &rc, 1))
      return -1;

    try
    {
      if(m_theToolTipBtnClose.IsEmpty()) m_theToolTipBtnClose = _T("Close");
      if(m_theToolTipBtnMenu.IsEmpty()) m_theToolTipBtnMenu = _T("Open study list");
      if(m_theToolTipBtnLeftScroll.IsEmpty()) m_theToolTipBtnLeftScroll = _T("Scroll Left");
      if(m_theToolTipBtnRightScroll.IsEmpty()) m_theToolTipBtnRightScroll = _T("Scroll Right");
    }
    catch(CMemoryException* e)
    {
      e->Delete();
      return -1;
    }
  }

  m_theResType = params->theResType;
  m_theMonitorIndex = params->theMonitorIndex;
  
  CStudyTabProperty* TP = CStudyTabProperty::getInstance();
  CStudyTabProperty::CFontProperty fontProp = TP->getFontProperty(m_theResType);

  m_theFont.CreateFont(fontProp.thePointSize, 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS,
    CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontProp.theFaceName);

  m_theMaxNumOfTabs = TP->getMaxNumTabs();

  return 0;
}

void CStudyTab::OnDestroy()
{
  CWnd::OnDestroy();

  StopScrolling();
  m_theHoverdItem = m_thePushedItem = m_theCurSelItem = nullptr;
  m_theItemsOffset = 0;
  m_isPartialView = m_isScrollLeftAllowd = m_isScrollRightAllowd = FALSE;
}


void CStudyTab::OnSize(UINT nType, int cx, int cy)
{
  CWnd::OnSize(nType, cx, cy);

  rearrange();
}


void CStudyTab::OnPaint()
{
  CPaintDC dc(this); // device context for painting

  if(m_theDrawManger == nullptr) return;

  if(m_theItemList.empty()) return;

  CRect clipBox;
  dc.GetClipBox(&clipBox);
  CRect rcClient;
  GetClientRect(&rcClient);
  CRect rcClip;
  rcClip.IntersectRect(&clipBox, &rcClient);

  TabPaintDC tabDC(dc, rcClip );

  CFont* oldFont = (CFont*)tabDC.SelectObject(GetFont());

  CRect itemClipRect;
  for(auto pos = m_theItemList.begin(), end = m_theItemList.end();
      pos != end;
      ++pos)
  {
    if(isItemVisible(*pos, NULL) && itemClipRect.IntersectRect(&rcClip, &((*pos)->rect)))
      m_theDrawManger->drawItem(*this, tabDC, *(*pos));
  }


  if(!m_theScrollLeftBtnRect.IsRectEmpty() && itemClipRect.IntersectRect(&rcClip, &m_theScrollLeftBtnRect))
  {
    BOOL isHover = m_theHoverdItem == m_theLeftScrollBtnItem && 
                   (m_thePushedItem == nullptr || m_thePushedItem == m_theLeftScrollBtnItem);
    m_theDrawManger->drawScrollLeftButton(*this, tabDC, m_theScrollLeftBtnRect, isHover, m_thePushedItem == m_theLeftScrollBtnItem, m_isScrollLeftAllowd);
    
    isHover = m_theHoverdItem == m_theRightScrollBtnItem &&
             (m_thePushedItem == nullptr || m_thePushedItem == m_theRightScrollBtnItem);
    m_theDrawManger->drawScrollRightButton(*this, tabDC, m_theScrollRightBtnRect, isHover, m_thePushedItem == m_theRightScrollBtnItem, m_isScrollRightAllowd);
  }
  if(!m_theMenuBtnRect.IsRectEmpty() && itemClipRect.IntersectRect(&rcClip, &m_theMenuBtnRect))
  {
    BOOL isHover = m_theHoverdItem == m_theMenuBtnItem && 
      (m_thePushedItem == NULL || m_thePushedItem == m_theMenuBtnItem);
    m_theDrawManger->drawMenuButton(*this, tabDC, m_theMenuBtnRect, isHover, m_thePushedItem == m_theMenuBtnItem, m_isPartialView);
  }
  if(!m_theCloseBtnRect.IsRectEmpty() && itemClipRect.IntersectRect(&rcClip, &m_theCloseBtnRect))
  {
    BOOL isHover = m_theHoverdItem == m_theCloseBtnItem &&
      (m_thePushedItem == nullptr || m_thePushedItem == m_theCloseBtnItem);
    m_theDrawManger->drawCloseButton(*this, tabDC, m_theCloseBtnRect, isHover, m_thePushedItem == m_theCloseBtnItem);
  }
}


BOOL CStudyTab::OnEraseBkgnd(CDC* pDC)
{
  if(m_theDrawManger != nullptr)
  {
    m_theDrawManger->drawCtrlBack(*this, *pDC);
    return TRUE;
  }

  return CWnd::OnEraseBkgnd(pDC);
}


void CStudyTab::OnLButtonDown(UINT nFlags, CPoint point)
{
  if(m_theHoverdItem != nullptr)
  {
    if(!isSystemButton(m_theHoverdItem))
    {
      BOOL isSelected = FALSE;

      m_thePushedItem = m_theHoverdItem;
      SetCapture();

      isSelected = (getSel() != m_theHoverdItem);

      if(isSelected)
      {
        CRect oldSelRect = getItemRect(getSel());
        CRect curSelRect = m_thePushedItem->rect;
        CRect invalidRect;
        invalidRect.IntersectRect(oldSelRect, curSelRect);
        setSel(m_thePushedItem);
        InvalidateRect(&invalidRect, FALSE);
        //TODO(Jong Min, Lee):  occurs event
      }
    }
    else
    {
      m_thePushedItem = m_theHoverdItem;

      if(m_thePushedItem == m_theLeftScrollBtnItem)
      {
        StepLeft();
        SetTimer(TIMER_ID_LEFT_SCROLL_CLICK, 300, NULL);
        rearrange();
      }
      else if(m_thePushedItem == m_theRightScrollBtnItem)
      {
        StepRight();
        SetTimer(TIMER_ID_RIGHT_SCROLL_CLICK, 300, NULL);
        rearrange();
      }
      Invalidate(TRUE);
      SetCapture();
    }
  }
  CWnd::OnLButtonDown(nFlags, point);
}


void CStudyTab::OnMouseMove(UINT nFlags, CPoint point)
{
  assignHoverItem(point);

  CWnd::OnMouseMove(nFlags, point);
}


void CStudyTab::OnLButtonUp(UINT nFlags, CPoint point)
{
  if(m_thePushedItem != nullptr)
  {
    BOOL close = (m_thePushedItem == m_theCloseBtnItem && m_theCloseBtnRect.PtInRect(point));
    BOOL menu = (m_thePushedItem == m_theMenuBtnItem && m_theMenuBtnRect.PtInRect(point));

    ::ReleaseCapture();

    StopScrolling();
    Invalidate(FALSE);
    if(close)
    {
      CPoint pt(point);
      ClientToScreen(&pt);

      m_thePushedItem = m_theCloseBtnItem;
      //TODO(Jong Min, Lee): notify
      m_thePushedItem = nullptr;
      Invalidate(FALSE);
    }
    else if(menu)
    {
      CPoint pt(point);
      ClientToScreen(&pt);
      m_thePushedItem = m_theMenuBtnItem;
      //TODO(Jong Min, Lee): notify
      m_thePushedItem = nullptr;
      Invalidate(FALSE);
    }
  }
  CWnd::OnLButtonUp(nFlags, point);
}


void CStudyTab::OnMouseLeave()
{
  if(m_theHoverdItem != nullptr)
  {
    m_theHoverdItem = NULL;
    Invalidate(FALSE);
  }
  CWnd::OnMouseLeave();
}


void CStudyTab::OnTimer(UINT_PTR nIDEvent)
{
  switch(nIDEvent)
  {
  case TIMER_ID_LEFT_SCROLL_CLICK:
    KillTimer(TIMER_ID_LEFT_SCROLL_CLICK);
    if(m_thePushedItem == m_theLeftScrollBtnItem)
      SetTimer(TIMER_ID_LEFT_SCLLOING, 20, NULL);
    break;

  case TIMER_ID_LEFT_SCLLOING:
    if(m_theHoverdItem == m_theLeftScrollBtnItem)
    {
      StepLeft();
      update(TRUE);
    }
    break;

  case TIMER_ID_RIGHT_SCROLL_CLICK:
    KillTimer(TIMER_ID_RIGHT_SCROLL_CLICK);
    if(m_thePushedItem == m_theRightScrollBtnItem)
      SetTimer(TIMER_ID_RIGHT_SCROLLING, 20, NULL);
    break;

  case TIMER_ID_RIGHT_SCROLLING:
    if(m_theHoverdItem == m_theRightScrollBtnItem)
    {
      StepRight();
      update(TRUE);
    }
    break;
  }

  CWnd::OnTimer(nIDEvent);
}

void CStudyTab::OnCaptureChanged(CWnd *pWnd)
{
  if(pWnd != this)
  {
    StopScrolling();
    Invalidate(TRUE);
  }
  CWnd::OnCaptureChanged(pWnd);
}

CStudyTab::TabPaintDC::TabPaintDC(CDC& dstDC, const CWnd& dstWnd)
{
  dstWnd.GetClientRect(&m_theDstRect);
  m_theSuccess = init(dstDC);
}

CStudyTab::TabPaintDC::TabPaintDC(CDC& dstDC, const CRect& dstRect)
{
  m_theDstRect = dstRect;
  m_theSuccess = init(dstDC);
}

CStudyTab::TabPaintDC::~TabPaintDC()
{
  if(m_theSuccess)
  {
    m_theDstDC->BitBlt(m_theDstRect.left, m_theDstRect.top, m_theDstRect.Width(), m_theDstRect.Height(),
                       this, m_theDstRect.left, m_theDstRect.top, SRCCOPY);
    SelectObject(m_theOldBmp);
  }
  else
    Detach();
}

BOOL CStudyTab::TabPaintDC::init(CDC& dstDC)
{
  m_theDstDC = &dstDC;

  BOOL res = isDoubleBufferingEnable() &&
             CreateCompatibleDC(m_theDstDC) &&
             m_theBmpSrc.CreateCompatibleBitmap(m_theDstDC, m_theDstRect.Width(), m_theDstRect.Height());
  if(res)
    m_theOldBmp = SelectObject(&m_theBmpSrc);
  else
  {
    if(m_hDC != nullptr) DeleteDC();
    if(m_theBmpSrc.m_hObject != NULL) m_theBmpSrc.DeleteObject();
    Attach(m_theDstDC->m_hDC);
  }
  SetBkMode(TRANSPARENT);
  return res;
}

CRect CStudyTab::Service::getItemHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return CRect(0, 0, 0, 0);
}

CRect CStudyTab::Service::getCloseBtnHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return CRect(2, 0, 2, 0);
}

CRect CStudyTab::Service::getMenuBtnHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return CRect(0,0,0,0);
}

CRect CStudyTab::Service::getLeftScrollBtnHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return CRect(0,0,0,0);
}

CRect CStudyTab::Service::getRightScrollBtnHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return CRect(0,0,0,0);
}

const CStudyTabItem* CStudyTab::Service::hitTest(CStudyTab& tabCtrl, IStudyTabBehavior& base, const CPoint& point)
{
  if(CRect(tabCtrl.getItemsRect()).PtInRect(point))
  {
    for(unsigned i = 0, c = tabCtrl.getNumOfItems(); i < c; ++i)
    {
      const CStudyTabItem* item = tabCtrl.getItem(i);
      if(item->rect.PtInRect(point))
        return item;
    }
  }

  return nullptr;
}

CToolTipCtrl* CStudyTab::Service::createToolTip(CStudyTab& tabCtrl, IStudyTabToolTip& base)
{
  if(!m_theToolTipCtrl.Create(&tabCtrl, TTS_ALWAYSTIP)) return nullptr;

  DWORD dwClassStyle = ::GetClassLong(m_theToolTipCtrl.m_hWnd, GCL_STYLE);
  dwClassStyle |= 0x00020000/*CS_DROPSHADOW*/;
  ::SetClassLong(m_theToolTipCtrl.m_hWnd, GCL_STYLE, dwClassStyle);

  return &m_theToolTipCtrl;
}

CRect CStudyTabRearrangeStub::getItemHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return base.getItemHorzMargin(tabCtrl, base);
}

CRect CStudyTabRearrangeStub::getCloseBtnHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return base.getCloseBtnHorzMargin(tabCtrl, base);
}

CRect CStudyTabRearrangeStub::getMenuBtnHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return base.getMenuBtnHorzMargin(tabCtrl, base);
}

CRect CStudyTabRearrangeStub::getLeftScrollBtnHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return base.getLeftScrollBtnHorzMargin(tabCtrl, base);
}

CRect CStudyTabRearrangeStub::getRightScrollBtnHorzMargin(CStudyTab& tabCtrl, IStudyTabRearrange& base)
{
  return base.getRightScrollBtnHorzMargin(tabCtrl, base);
}

const CStudyTabItem* CStudyTabBehaviorStub::hitTest(CStudyTab& tabCtrl, IStudyTabBehavior& base, const CPoint& point)
{
  return base.hitTest(tabCtrl, base, point);
}

CStudyTabDraw* CStudyTabStyleBase::getDrawManager()
{
  return this;
}

IStudyTabRearrange* CStudyTabStyleBase::getRearrangeManager()
{
  return nullptr;
}

IStudyTabBehavior* CStudyTabStyleBase::getBehaviorManager()
{
  return nullptr;
}

IStudyTabToolTip* CStudyTabStyleBase::getToolTipManager()
{
  return nullptr;;
}

void CStudyTabStyleBase::drawCtrlBack(CStudyTab& tabCtrl, CDC& dc)
{
  CRect rcClient;
  tabCtrl.GetClientRect(&rcClient);

  COLORREF clr = RGB(45, 45, 48);
  dc.FillSolidRect(&rcClient, clr);
}

void CStudyTabStyleBase::drawItem(CStudyTab& tabCtrl, CDC& dc, const CStudyTabItem& item)
{
  //draw bk
  drawItemBK(tabCtrl, dc, item);

  //draw title
  drawItemTitle(tabCtrl, dc, item);
}

void CStudyTabStyleBase::drawItemBK(CStudyTab& tabCtrl, CDC& dc, const CStudyTabItem& item)
{
  BOOL select = (tabCtrl.getSel() == &item);
  BOOL hover = (tabCtrl.getHover() == &item);

  COLORREF clr;
  if(select)
    clr = RGB(0, 122, 204);
  else if(hover)
    clr = RGB(28, 151, 234);
  else
    clr = RGB(45, 45, 48);

  dc.FillSolidRect(&(item.rect), clr);
}

void CStudyTabStyleBase::drawItemTitle(CStudyTab& tabCtrl, CDC& dc, const CStudyTabItem& item)
{
  COLORREF oldTextClr = dc.GetTextColor();
  CRect titleRect(item.rect);
  dc.DrawText(item.theTitle, -1, &titleRect, DT_LEFT | DT_CENTER | DT_END_ELLIPSIS | DT_WORDBREAK | DT_CALCRECT);
  if(titleRect < item.rect)
  {
    CPoint ptCenter = item.rect.CenterPoint();
    int height = titleRect.Height();
    titleRect = item.rect;
    titleRect.top = ptCenter.y - height / 2;
    titleRect.bottom = ptCenter.y + height / 2;
  }
  dc.DrawText(item.theTitle, -1, &titleRect, DT_LEFT | DT_CENTER | DT_END_ELLIPSIS | DT_WORDBREAK);
}

void CStudyTabStyleBase::drawButtonFrame(CStudyTab& tabCtrl, CDC& dc, const CRect& rect, BOOL isHover, BOOL isPushed)
{
  COLORREF clr;
  if(isHover && isPushed)
    clr = RGB(0, 122, 204);
  else
    clr = RGB(62, 62, 64);

  dc.FillSolidRect(rect, clr);
}
void CStudyTabStyleBase::drawMarker(CDC& dc, const CRect& rect, CImageList* imageList, int imageIndex, COLORREF color)
{
  BOOL res = FALSE;

  HICON hIcon = imageList->ExtractIconW(imageIndex);
  //
  if(hIcon != NULL)
  {
    ICONINFO iconinfo;
    IMAGEINFO imginfo;
    //
    if(imageList->GetImageInfo(imageIndex, &imginfo) &&
      imginfo.hbmMask != NULL &&
      ::GetIconInfo(hIcon, &iconinfo))
    {
      res = drawMarker(dc, rect, iconinfo.hbmMask, color);
      //
      ::DeleteObject(iconinfo.hbmColor);
      ::DeleteObject(iconinfo.hbmMask);
    }
    ::DeleteObject(hIcon);
  }
  if(!res)
    imageList->Draw(&dc, imageIndex, CPoint(rect.left, rect.top), ILD_TRANSPARENT);
}

BOOL CStudyTabStyleBase::drawMarker(CDC& dc, const CRect& rect, HBITMAP hbmpMask, COLORREF color)
{
  BITMAP maskInfo;
  if(!::GetObject(hbmpMask, sizeof(maskInfo), &maskInfo))
    return FALSE;

  CSize szDst(min(rect.Width(), maskInfo.bmWidth), min(rect.Height(), maskInfo.bmHeight));
  //
  HDC dcMask = NULL, dc1 = NULL, dc2 = NULL;
  HBITMAP bmp1 = NULL, bmp2 = NULL;
  //
  BOOL res = FALSE;
  //
  if((dcMask = ::CreateCompatibleDC(dc.m_hDC))!=NULL &&
    (dc1 = ::CreateCompatibleDC(dc.m_hDC))!=NULL && 
    (dc2 = ::CreateCompatibleDC(dc.m_hDC))!=NULL &&
    (bmp1 = ::CreateCompatibleBitmap(dc.m_hDC,szDst.cx,szDst.cy))!=NULL && 
    (bmp2 = ::CreateCompatibleBitmap(dc.m_hDC,szDst.cx,szDst.cy))!=NULL)
  {
    HBITMAP bmpMaskOld = (HBITMAP)::SelectObject(dcMask,hbmpMask);
    HBITMAP bmpOld1 = (HBITMAP)::SelectObject(dc1,bmp1);
    HBITMAP bmpOld2 = (HBITMAP)::SelectObject(dc2,bmp2);
    // 
    ::BitBlt(dc1,0,0,szDst.cx,szDst.cy,dcMask,0,0,SRCCOPY);
    // 
    ::BitBlt(dc.m_hDC,rect.left,rect.top,szDst.cx,szDst.cy,dc1,0,0,SRCAND);
    // 
    ::BitBlt(dc1,0,0,szDst.cx,szDst.cy,NULL,0,0,DSTINVERT);
    // 
    HBRUSH br = ::CreateSolidBrush(color);
    HBRUSH brOld = (HBRUSH)::SelectObject(dc2,br);
    ::BitBlt(dc2,0,0,szDst.cx,szDst.cy,dc1,0,0,MERGECOPY);
    ::SelectObject(dc2,brOld);
    ::DeleteObject(br);
    // 
    ::BitBlt(dc.m_hDC,rect.left,rect.top,szDst.cx,szDst.cy,dc2,0,0,SRCPAINT);
    // 
    ::SelectObject(dcMask,bmpMaskOld);
    ::SelectObject(dc1,bmpOld1);
    ::SelectObject(dc2,bmpOld2);
    // 
    res = TRUE;
  }
  //
  if(bmp1!=NULL) ::DeleteObject(bmp1);
  if(bmp2!=NULL) ::DeleteObject(bmp2);
  if(dcMask!=NULL) ::DeleteDC(dcMask);
  if(dc1!=NULL) ::DeleteDC(dc1);
  if(dc2!=NULL) ::DeleteDC(dc2);
  //
  return res;
}



